<?php

Raise::load('core.RaiseObject');
Raise::load('core.RaiseCollection');
Raise::load('db.RaiseDbAdapter');
Raise::load('db.RaiseDbAdapterFactory');
Raise::load('db.RaiseDbTransact');

/**
 * RaiseDbConnection class
 * Database Connection Object that stores information about a specific data
 * access information.
 *
 * @author Sam-Mauris Yong <hellclanner at live dot com>
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.Core.DataAccess
 * @since 1.2
 */
class RaiseDbConnection extends RaiseObject {

    /**
     * The host / server to connect to
     * @var string
     */
    private $server;

    /**
     * The type of server that is on the other end of the line
     * @var string
     */
    private $serverType;

    /**
     * The initial catalog / database to use upon connection.
     * @var string
     */
    private $database;

    /**
     * The username assosciated with the connection that is needed for authentication.
     * @var string
     */
    private $username;

    /**
     * The password assosciated with the connection that is needed for authentication.
     * @var string
     */
    private $password;

    /**
     * Other options or information assosciated with the connection
     * @var RaiseCollection
     */
    private $options;

    /**
     * The RaiseDbAdapter in use
     * @var RaiseDbAdapter
     */
    private $adapter;

    /**
     * Reference to the last created connection
     * @var RaiseDbConnection
     */
    private static $lastConnection;
    
    /**
     * Constructs a new DataAccessConnection to a Data Source/Server/Pool
     * @param RaiseSettings $settings The settings that this connection will use
     * @param string $s The connection string or the name of the connection string in the configuration file
     */
    function __construct($settings, $s = 'default'){

        if($settings->get('Database', $s) != null){
            $s = $settings->get('Database', $s);
        }

        $this->options = new RaiseCollection();
        $a = array();
        $dividerpos = strpos($s, ':');
        $this->serverType(strtolower(substr($s, 0, $dividerpos)));
        if($this->serverType() != 'uri'){
            $t = array();
            $e = explode(';', substr($s, $dividerpos + 1));
            foreach($e as $ele){
                list($key, $value) = explode('=', $ele);
                $t[strtolower(trim($key))] = trim($value);
            }
            if(isset($t['port'])){
                $this->server($t['host'] . ':' . $t['port']);
            }else{
                $this->server($t['host']);
            }
            if(array_key_exists('dbname', $t)){
                $this->database($t['dbname']);
            }
            if(array_key_exists('user', $t)){
                $this->username($t['user']);
            }
            if(array_key_exists('pwd', $t)){
                $this->password($t['pwd']);
            }
            unset($t['host'], $t['port'], $t['dbname'], $t['user'], $t['pwd']);
            $this->options(new RaiseCollection($t));
        }else{
             $this->server(substr($s, $dividerpos));
        }

        $this->adapter(RaiseDbAdapterFactory::getAdapter($settings, $this->serverType()));

        self::$lastConnection = $this;
    }

    /**
     * Get or set the IP or host name of the server to connect to
     * @param string $s (optional) The new server name
     * @return string
     */
    public function server($s = null){
        if(func_num_args() == 1){
            $this->server = $s;
        }
        return $this->server;
    }

    /**
     * Get or set the server type
     * @param string $s (optional) The new server type
     * @return string
     */
    public function serverType($s = null){
        if(func_num_args() == 1){
            $this->serverType = $s;
        }
        return $this->serverType;
    }

    /**
     * Get or set the name of the database catalog to connect to
     * @param string $d (optional) The new database name
     * @return string
     */
    public function database($d = null){
        if(func_num_args() == 1){
            $this->database = $d;
        }
        return $this->database;
    }

    /**
     * Get or set the username to authenticate the connection
     * @param string $u (optional) The new username
     * @return string
     */
    public function username($u = null){
        if(func_num_args() == 1){
            $this->username = $u;
        }
        return $this->username;
    }

    /**
     * Get or set the password to authenticate the connection
     * @param string $p (optional) The new password
     * @return string
     */
    public function password($p = null){
        if(func_num_args() == 1){
            $this->password = $p;
        }
        return $this->password;
    }

    /**
     * Get or set the collection of options used in the connection
     * @param RaiseCollection $o (optional) The new collection of options
     * @return RaiseCollection
     */
    public function options($o = null){
        if(func_num_args() == 1){
            $this->options = $o;
        }
        return $this->options;
    }
    
    /**
     * Get the adapter that is used by this connection
     * @param RaiseDbAdapter $a (optional) The new adapter for this connection to use
     * @return RaiseDbAdapter
     */
    public function adapter($a = null){
        if(func_num_args() == 1){
            if($this->adapter != null){
                $this->adapter->connection(null);
            }
            $this->adapter = $a;
            if($this->adapter->connection() != $this){
                $this->adapter->connection($this);
            }
        }
        return $this->adapter;
    }
    /**
     * Open the appropriate connection if the connection was not made.
     * @return boolean Returns true or false based on whether the connection was successful or not.
     */
    public function connect(){
        if(!$this->adapter()->isConnected()){
            $this->adapter()->connect($this);
            if(!$this->adapter()->isConnected()){
                throw new RaiseRemoteConnectionException('Failed to connect to the remote database.');
            }
        }
        return $this->adapter()->isConnected();
    }

    /**
     * Disconnects the connection if it is connected.
     */
    public function disconnect(){
        if(!$this->isConnected()){
            throw new RaiseInvalidRequestException('DataAccessConnection cannot be disconnected because it is not connected in the first place.');
        }
        $this->adapter()->disconnect();
    }

    /**
     * Checks whether the connection is open or closed.
     * @return bool True if is connected, and false otherwise
     */
    public function isConnected(){
        return $this->adapter()->isConnected();
    }

    /**
     * Get the last result of the connection
     * @return object|resource
     */
    public function result(){
        return $this->adapter()->result();
    }
    
    /**
     * Gets the last INSERT operation ID
     * @return int Last Inserted ID
     */
    public function lastInsertId(){
        return $this->adapter()->lastInsertId();
    }

    /**
     * Fetch the last created connection for use
     * @return RaiseDbConnection
     */
    public static function last(){
        return self::$lastConnection;
    }

    /**
     * Get the RaiseDbTransact object for this connection
     * @return RaiseDbTransact
     */
    public function transaction(){
        static $transact = false;
        if(!$transact){
            $transact = new RaiseDbTransact($this);
        }
        return $transact;
    }
    
}

